package optiondef

import (
	"context"
	"fmt"
	"photowall/pkg/config"
	"photowall/pkg/log"
	"regexp"
	"sync"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
)

// 数据库使用的数据结构封装
type AlbumInfo struct {
	AlbumCoverURL string `json:"album_cover_url" bson:"album_cover_url"`
	AlbumID       string `json:"album_id" bson:"album_id"` // 添加了 AlbumID 相关的 bson 标签
	AuthorID      string `json:"author_id" bson:"author_id"`
	AlbumName     string `json:"album_name" bson:"album_name"`
	CreateDate    string `json:"create_date" bson:"create_date"`
	Description   string `json:"description" bson:"description"`
}

// 图片数据结构
type Photoinfo struct {
	Author       string `json:"author"`
	AuthorID     string `json:"author_id"`
	Description  string `json:"description"`
	PhotoName    string `json:"photo_name"`
	PhotoID      string `json:"photo_id"`
	Public       int64  `json:"public"`
	ThumbnailURL string `json:"thumbnail_url"`
	URL          string `json:"url"`
}

const (
	ErrCodeForUpdateAlbumInDbByAuthorIDAndAlbumName int = iota
)

// 定义一个自定义错误类型
type AlbumDbServiceError struct {
	Code    int
	Message string
	Err     error
}

// 实现 Unwrap() 方法，使其可以被 errors.Unwrap 使用
func (e *AlbumDbServiceError) Unwrap() error {
	return e.Err
}

// 实现 Error 方法
func (e *AlbumDbServiceError) Error() string {
	return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}

type albumDbService struct {
	AlbumDbService
	PhotoDBService
}

var (
	mongodbinstance     *albumDbService
	mongodbinstanceOnce sync.Once
)

func getAlbumdbService() *albumDbService {
	mongodbinstanceOnce.Do(func() {
		mongodbinstance = &albumDbService{
			AlbumDbService: &albumDbServiceImpl{},
			PhotoDBService: &photoDBServiceImpl{},
		}
	})

	return mongodbinstance
}

// AlbumDbService 定义了与相册数据库操作相关的接口
type AlbumDbService interface {
	// AddAlbumToDb 将相册信息插入到数据库中
	AddAlbumToDb(info *AlbumInfo) (res bool, err error)

	// IsAlbumInDbByAuthorIDAndAlbumName 检查指定作者和名称的相册是否存在于数据库中
	IsAlbumInDbByAuthorIDAndAlbumName(authorid string, albumName string) (res bool, err error)

	// DeleteAlbumInDbByAuthorIDAndAlbumName 根据作者ID和相册名称删除数据库中的相册
	DeleteAlbumInDbByAuthorIDAndAlbumName(authorid string, albumName string) (res bool, err error)

	// GetAlbumsByAuthIDInDb 根据作者ID从数据库中获取相册信息
	GetAlbumsByAuthIDInDb(id string) (albumInfo []AlbumInfo, err error)

	// UpdateAlbumInDbByAuthorIDAndAlbumName 根据作者ID和相册名称更新数据库中的相册信息
	UpdateAlbumInDbByAuthorIDAndAlbumName(authorid string, albumName string, info *AlbumInfo) (res bool, err error)

	// GetPubAlbumByNameInDb 根据相册名称从数据库中获取相册信息
	GetPubAlbumByNameInDb(albumName string) ([]AlbumInfo, error)

	GetAlbumInfoByAuthorIDAlbumNameInMongoDB(authorid string, albumName string) (*AlbumInfo, error)
}

type albumDbServiceImpl struct{}

// GetPubAlbumByNameInDb retrieves albums by their name from the database using regex pattern matching
func (albumDbService *albumDbServiceImpl) GetPubAlbumByNameInDb(albumName string) ([]AlbumInfo, error) {
	// 校验相册名字符串是否为空
	if albumName == "" {
		return nil, fmt.Errorf("album name string cannot be empty")
	}

	// 构建正则表达式模式
	albumNamePattern := fmt.Sprintf(".*%s.*", regexp.QuoteMeta(albumName))

	// 从数据库中查询相册信息，使用正则表达式匹配
	filter := bson.M{
		"album_name": bson.M{"$regex": albumNamePattern, "$options": "i"},
		"public":     1,
	}
	cursor, err := getAlbumDBCollectionInMongodb().Find(context.Background(), filter)
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("Failed to query albums by name pattern: %v", err))
		return nil, fmt.Errorf("failed to query albums by name pattern: %w", err)
	}
	defer cursor.Close(context.Background())

	// 将查询结果转换为切片
	var albums []AlbumInfo
	if err = cursor.All(context.Background(), &albums); err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("Failed to decode albums: %v", err))
		return nil, fmt.Errorf("failed to decode albums: %w", err)
	}

	return albums, nil
}

// 单例模式：获取一个相册的collection单例对象
func getAlbumDBCollectionInMongodb() *mongo.Collection {
	dbName := config.GetConfig().WechatUserConfig.DbName
	collectionName := config.GetConfig().WechatUserConfig.AlbumDbCollection

	// 检查数据库名称和集合名称是否为空
	if dbName == "" {
		log.GetDatabaseLogger().Error("Database name is empty")
		panic(`Database name is empty`)
	}
	if collectionName == "" {
		log.GetDatabaseLogger().Error("Collection name is empty")
		panic(`Collection name is empty`)
	}

	return getMongodbClient().Database(dbName).Collection(collectionName)
}

// add: 将信息其插入到数据库
func (albumDbService *albumDbServiceImpl) AddAlbumToDb(info *AlbumInfo) (bool, error) {
	// valid check
	if len(info.AlbumName) == 0 || len(info.AuthorID) == 0 {
		return false, fmt.Errorf(`AddAlbumToDb info is not valid, %v`, *info)
	}

	res, err := addAlbumToMongoDB(info)
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf(`AddAlbumToDb failed, %v`, *info))
		return false, err
	}

	if !res {
		return false, fmt.Errorf(`AddAlbumToDb failed but no err, %v`, info)
	}

	return true, nil
}

// add: 插入相册到数据库的子方法
func addAlbumToMongoDB(info *AlbumInfo) (bool, error) {
	_, err := getAlbumDBCollectionInMongodb().InsertOne(context.Background(), info)
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("can not insert db, %v, err: %v", *info, err.Error()))
		return false, err
	}

	return true, nil
}

// get: 通过册的主人ID和相册的名字查找是否在数据库中
func (albumDbService *albumDbServiceImpl) IsAlbumInDbByAuthorIDAndAlbumName(authorid string, albumName string) (res bool, err error) {
	data, err := albumDbService.GetAlbumInfoByAuthorIDAlbumNameInMongoDB(authorid, authorid)
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf(`IsUserInDb failed, %v, err:%v`, authorid+" "+albumName, err.Error()))
		return false, err
	}

	if data == nil {
		return false, nil
	}

	return true, nil
}

// get: 通过相册的主人ID和相册的名字查找相册信息, 这个结果是唯一的
func (albumDbService *albumDbServiceImpl) GetAlbumInfoByAuthorIDAlbumNameInMongoDB(authorid string, albumName string) (*AlbumInfo, error) {
	var res AlbumInfo
	err := getAlbumDBCollectionInMongodb().FindOne(context.Background(), bson.M{
		"author_id":  authorid,
		"album_name": albumName,
	}).Decode(&res)

	if err != nil {
		if err == mongo.ErrNoDocuments {
			return nil, nil
		}
		log.GetDatabaseLogger().Error(fmt.Sprintf("can not exec FindOne option, %v, err:%v", authorid+" "+albumName, err.Error()))
		return nil, err
	}

	return &res, nil
}

// delete: 通过主人的id和相册名
func (albumDbService *albumDbServiceImpl) DeleteAlbumInDbByAuthorIDAndAlbumName(authorid string, albumName string) (res bool, err error) {
	// info valid check
	if len(authorid) == 0 || len(albumName) == 0 {
		return false, fmt.Errorf(`DeleteAlbumInDb info is not valid, %v`, authorid+" "+albumName)
	}

	_, err = deleteAlbumInfoByAuthorIDAlbumNameInMongoDB(authorid, albumName)
	if err != nil {
		log.GetDatabaseLogger().Error(`deleteAlbumInfoByAuthorIDAlbumNameInMongoDB, :` + err.Error())
		return false, err
	}

	return true, nil
}

// delete: 通过相册的主人ID和相册的名字删除相册信息, 这个结果是唯一的
func deleteAlbumInfoByAuthorIDAlbumNameInMongoDB(authorid string, albumName string) (bool, error) {
	_, err := getAlbumDBCollectionInMongodb().DeleteOne(context.Background(), bson.M{
		"author_id":  authorid,
		"album_name": albumName,
	})
	if err != nil {
		return false, err
	}

	return true, nil
}

// get: 获取一个用户下所有的相册信息
func (albumDbService *albumDbServiceImpl) GetAlbumsByAuthIDInDb(id string) (userinfo []AlbumInfo, err error) {
	return getUserAlbumsInfoInMongoDB(id)
}

// get: 通过用户的ID获取其名下所有的相册名
func getUserAlbumsInfoInMongoDB(authorID string) ([]AlbumInfo, error) {
	var res []AlbumInfo

	cursor, err := getAlbumDBCollectionInMongodb().Find(context.Background(), bson.M{
		"author_id": authorID,
	})
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf(`func getUserAlbumsInfoInMongoDB failed. %v, %v`, err.Error(), authorID))
		return nil, err
	}

	defer cursor.Close(context.Background())

	if err := cursor.All(context.Background(), &res); err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf(`func getUserAlbumsInfoInMongoDB failed. %v, %v`, err.Error(), authorID))
		return nil, err
	}

	return res, nil
}

// update: 通过相册的主人ID和相册的名字
func (albumDbService *albumDbServiceImpl) UpdateAlbumInDbByAuthorIDAndAlbumName(authorid string, albumName string, info *AlbumInfo) (res bool, err error) {
	// 查看相册名是否一样，不一样则检查新相册名是否存在，不存在则允许修改，存在则报错
	if albumName == info.AlbumName {
		exist, err := albumDbService.IsAlbumInDbByAuthorIDAndAlbumName(authorid, info.AlbumName)
		if err != nil {
			return false, &AlbumDbServiceError{
				Code:    ErrCodeForUpdateAlbumInDbByAuthorIDAndAlbumName,
				Message: "target album name is exist",
				Err:     err,
			}
		}
		if exist {
			return false, fmt.Errorf(`can not `)
		}
	}

	// 正常更新相册
	filter := bson.M{
		"author_id":  authorid,
		"album_name": albumName,
	}
	data, err := bson.Marshal(info)
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf(`UpdateAlbumInDbByAuthorIDAndAlbumName can not format info data, %v`, *info))
		return false, fmt.Errorf(`UpdateAlbumInDbByAuthorIDAndAlbumName can not format info data, %v`, *info)
	}
	uptate := bson.M{
		"$set": data,
	}

	_, err = getAlbumDBCollectionInMongodb().UpdateOne(context.Background(), filter, uptate)
	if err != nil {
		log.GetDatabaseLogger().Error(`UpdateAlbumInDbByAuthorIDAndAlbumName can not update`)
		return false, err
	}

	return true, nil
}

type PhotoDBService interface {
	// add
	AddPhotoToAlbumByAlbumID(albumID string, photo *Photoinfo) (bool, error)
	// del
	DeletePhotoByPhotoID(photoID string) (bool, error)
	// get
	GetAllPhotoInAlbumByAlbumID([]Photoinfo, string) (bool, error)
	GetAllPhotoInAlbumByAlbumName(albumPhotos []Photoinfo, albumName string) (bool, error)
	GetPhotoByPhotoID(photoID string) (*Photoinfo, error)
	GetPublicPhotosByName(photoName string) ([]Photoinfo, error)
	GetUserPhotosByName(authorID, name string) ([]Photoinfo, error)

	// update
	UpdatePhotoInfoByPhotoID(photoID string, info *Photoinfo) (bool, error)

	// is
	IsPhotoOwnedByUser(photoID, userID string) (bool, error)
}

type photoDBServiceImpl struct{}

// GetPublicPhotosByName: 通过名字查找公开的照片
func (albumDbService *photoDBServiceImpl) GetPublicPhotosByName(photoName string) ([]Photoinfo, error) {
	// 验证 name 的有效性
	if len(photoName) == 0 {
		return nil, fmt.Errorf("GetPublicPhotosByName name is not valid")
	}

	// 构建正则表达式
	regexPattern := fmt.Sprintf(".*%s.*", regexp.QuoteMeta(photoName))
	regex := bson.M{"$regex": regexPattern, "$options": "i"}

	// 查找公开的照片
	filter := bson.M{
		"photo_name": regex,
		"public":     1,
	}
	cursor, err := getPhotoDBCollectionInMongodb().Find(context.Background(), filter)
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("GetPublicPhotosByName failed, %v", err))
		return nil, err
	}

	var photos []Photoinfo
	if err = cursor.All(context.Background(), &photos); err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("GetPublicPhotosByName cursor.All failed, %v", err))
		return nil, err
	}

	return photos, nil
}

// GetUserPhotosByName: 通过名字查找用户自己的照片
func (albumDbService *photoDBServiceImpl) GetUserPhotosByName(authorID, PhotoName string) ([]Photoinfo, error) {
	// 验证 authorID 和 name 的有效性
	if len(authorID) == 0 || len(PhotoName) == 0 {
		return nil, fmt.Errorf("GetUserPhotosByName info is not valid")
	}

	// 构建正则表达式
	regexPattern := fmt.Sprintf(".*%s.*", regexp.QuoteMeta(PhotoName))
	regex := bson.M{"$regex": regexPattern, "$options": "i"}

	// 查找用户自己的照片
	filter := bson.M{
		"author_id":  authorID,
		"photo_name": regex,
	}
	cursor, err := getPhotoDBCollectionInMongodb().Find(context.Background(), filter)
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("GetUserPhotosByName failed, %v", err))
		return nil, err
	}

	var photos []Photoinfo
	if err = cursor.All(context.Background(), &photos); err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("GetUserPhotosByName cursor.All failed, %v", err))
		return nil, err
	}

	return photos, nil
}

// AddPhotoToAlbumByAlbumID: 向指定相册添加照片
func (albumDbService *photoDBServiceImpl) AddPhotoToAlbumByAlbumID(albumID string, photo *Photoinfo) (bool, error) {
	// 验证相册ID和照片信息的有效性
	if len(albumID) == 0 || photo == nil || len(photo.URL) == 0 || len(photo.PhotoName) == 0 {
		return false, fmt.Errorf("AddPhotoToAlbumByAlbumID info is not valid")
	}

	// 插入照片到数据库
	_, err := getPhotoDBCollectionInMongodb().InsertOne(context.Background(), photo)
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("AddPhotoToAlbumByAlbumID failed, %v", err))
		return false, err
	}

	return true, nil
}

// IsPhotoOwnedByUser: 通过照片的ID查看该相册是否属于某个userid
func (albumDbService *photoDBServiceImpl) IsPhotoOwnedByUser(photoID, userID string) (bool, error) {
	// 验证 photoID 和 userID 的有效性
	if len(photoID) == 0 || len(userID) == 0 {
		return false, fmt.Errorf("IsAlbumOwnedByUser info is not valid")
	}

	// 查找照片
	filter := bson.M{
		"photo_id":  photoID,
		"author_id": userID,
	}

	count, err := getPhotoDBCollectionInMongodb().CountDocuments(context.Background(), filter)
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("IsAlbumOwnedByUser failed, %v", err))
		return false, err
	}

	return count > 0, nil
}

// DeletePhotoByPhotoID: 通过照片ID删除照片
func (albumDbService *photoDBServiceImpl) DeletePhotoByPhotoID(photoID string) (bool, error) {
	if len(photoID) == 0 {
		return false, fmt.Errorf("DeletePhotoByPhotoID info is not valid")
	}

	_, err := getPhotoDBCollectionInMongodb().DeleteOne(context.Background(), bson.M{"photo_id": photoID})
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("DeletePhotoByPhotoID failed, %v", err))
		return false, err
	}

	return true, nil
}

// GetAllPhotoInAlbumByAlbumID: 获取指定相册中的所有照片
func (albumDbService *photoDBServiceImpl) GetAllPhotoInAlbumByAlbumID(albumPhotos []Photoinfo, albumID string) (bool, error) {
	cursor, err := getPhotoDBCollectionInMongodb().Find(context.Background(), bson.M{"album_id": albumID})
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("GetAllPhotoInAlbumByAlbumID failed, %v", err))
		return false, err
	}
	defer cursor.Close(context.Background())

	if err := cursor.All(context.Background(), &albumPhotos); err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("GetAllPhotoInAlbumByAlbumID failed, %v", err))
		return false, err
	}

	return true, nil
}

// GetAllPhotoInAlbumByAlbumName: 获取指定相册名称中的所有照片
func (albumDbService *photoDBServiceImpl) GetAllPhotoInAlbumByAlbumName(albumPhotos []Photoinfo, albumName string) (bool, error) {
	cursor, err := getPhotoDBCollectionInMongodb().Find(context.Background(), bson.M{"album_name": albumName})
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("GetAllPhotoInAlbumByAlbumName failed, %v", err))
		return false, err
	}
	defer cursor.Close(context.Background())

	if err := cursor.All(context.Background(), &albumPhotos); err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("GetAllPhotoInAlbumByAlbumName failed, %v", err))
		return false, err
	}

	return true, nil
}

// GetPhotoByPhotoID: 通过照片ID获取照片信息
func (albumDbService *photoDBServiceImpl) GetPhotoByPhotoID(photoID string) (*Photoinfo, error) {
	var photo Photoinfo
	err := getPhotoDBCollectionInMongodb().FindOne(context.Background(), bson.M{"photo_id": photoID}).Decode(&photo)
	if err != nil {
		if err == mongo.ErrNoDocuments {
			return nil, nil
		}
		log.GetDatabaseLogger().Error(fmt.Sprintf("GetPhotoByPhotoID failed, %v", err))
		return nil, err
	}

	return &photo, nil
}

// UpdatePhotoInfoByPhotoID: 更新照片信息
func (albumDbService *photoDBServiceImpl) UpdatePhotoInfoByPhotoID(photoID string, photo *Photoinfo) (bool, error) {
	if len(photoID) == 0 || photo == nil || len(photo.URL) == 0 || len(photo.PhotoName) == 0 {
		return false, fmt.Errorf("UpdatePhotoInfoByPhotoID info is not valid")
	}

	updateData, err := bson.Marshal(photo)
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("UpdatePhotoInfoByPhotoID failed, %v", err))
		return false, err
	}

	_, err = getPhotoDBCollectionInMongodb().UpdateOne(context.Background(), bson.M{"photo_id": photoID}, bson.M{"$set": updateData})
	if err != nil {
		log.GetDatabaseLogger().Error(fmt.Sprintf("UpdatePhotoInfoByPhotoID failed, %v", err))
		return false, err
	}

	return true, nil
}

// 辅助函数：获取照片集合
func getPhotoDBCollectionInMongodb() *mongo.Collection {
	dbName := config.GetConfig().WechatUserConfig.DbName
	collectionName := config.GetConfig().WechatUserConfig.PhotoDbCollection // 照片集合名称

	if dbName == "" || collectionName == "" {
		log.GetDatabaseLogger().Error("Database name or Collection name is empty")
		panic("Database name or Collection name is empty")
	}

	return getMongodbClient().Database(dbName).Collection(collectionName)
}
